Skip to content

Fix UB on empty trampoline template entries#112

Merged
GJDuck merged 1 commit into
GJDuck:masterfrom
ehgus607:master
Jun 28, 2026
Merged

Fix UB on empty trampoline template entries#112
GJDuck merged 1 commit into
GJDuck:masterfrom
ehgus607:master

Conversation

@ehgus607

Copy link
Copy Markdown
Contributor

Description

This PR fixes an undefined behavior in empty trampoline template handling.

Root Cause

When template is empty ([]), entries in e9json.cpp:201 can be empty, but code still accessed entries[0] in trampoline duplication. (e9json.cpp:210) That is out-of-bounds access (UB).

Reproduction

  1. Build with debug STL checks:
    make clean && CXXFLAGS='-D_GLIBCXX_DEBUG' make -j$(nproc) debug
  2. Run:
    ./e9tool --backend ./e9patch -M 1 -P empty <binary>

Before fix: abort with vector out-of-bounds message.

hash@hash:~/e9patch$ ./e9tool --backend ./e9patch -M 1 -P empty /bin/true
/usr/include/c++/11/debug/vector:445:
In function:
    std::debug::vector<_Tp, _Allocator>::const_reference std::
    debug::vector<_Tp, _Allocator>::operator[](std::debug::vector<_Tp, 
    _Allocator>::size_type) const [with _Tp = Entry; _Allocator = 
    std::allocator<Entry>; std::debug::vector<_Tp, 
    _Allocator>::const_reference = const Entry&; std::debug::vector<_Tp, 
    _Allocator>::size_type = long unsigned int]

Error: attempt to subscript container with out-of-bounds index 0, but 
container only holds 0 elements.

Objects involved in the operation:
    sequence "this" @ 0x7ffd26853c40 {
      type = std::debug::vector<Entry, std::allocator<Entry> >;
    }

Fix

  • Add a guard so copy is done only when num_entries > 0.
  • This avoids accessing entries[0] when entries is empty.

Comment thread src/e9patch/e9json.cpp
T->num_entries = num_entries;
memcpy(T->entries, &entries[0], num_entries * sizeof(Entry));
if (num_entries > 0)
memcpy(T->entries, entries.data(), num_entries * sizeof(Entry));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi, just want to note that the branching is not necessary here (and on whether it's desirable, I don't have any informed opinion).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error message in my description is from libstdc++ debug mode. I also checked with UBSan, and it shows an error message for this exact same access. Accessing index 0 of an empty vector is an Undefined Behavior according to the C++ standard.

I agree that this bug might not be very critical in real situations. However, considering memory safety, I think it is better to keep this branch to prevent the UB explicitly.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think @McSinyx was just pointing out that the branch if (num_entries > 0) could be technically removed from the patch, since if num_entries == 0 then entries.data() is still defined and memcpy is a nop. The original code was clearly UB however.

@GJDuck GJDuck merged commit 682ab1f into GJDuck:master Jun 28, 2026
1 check passed
@GJDuck

GJDuck commented Jun 28, 2026

Copy link
Copy Markdown
Owner

Thanks for the report and patch.

@McSinyx: you are right, but it's fine either way.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants